﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.IO;
using System.Text;
using System.ComponentModel;
using System.Windows.Media.Imaging;
using System.Threading;
using System.Security.Cryptography;
using System.Windows.Threading;
using System.Windows;
using BrainTechLLC;
using BrainTechLLC.ThreadSafeObjects;
using AForge;
using AForge.Imaging;
using AForge.Imaging.Filters;
using System.Collections.ObjectModel;
using System.Windows.Media;

namespace WpfImageDuplicates
{
    public class FilterCollection : ObservableCollection<SingleFilter>, INotifyPropertyChanged
    {
        public event EventHandler GenerationChanged;
        protected int _currentGeneration;

        protected object _lockObj = new object();

        protected bool _active = true;

        public int CurrentGeneration
        {
            get { return _currentGeneration; }
            set
            {
                if (_currentGeneration != value)
                {
                    _currentGeneration = value;

                    if (GenerationChanged != null)
                        GenerationChanged(this, EventArgs.Empty);

                    OnPropertyChanged(new PropertyChangedEventArgs("CurrentGeneration"));
                }
            }
        }

        public void GotoNextGeneration()
        {
            CurrentGeneration += 1;
        }

        public bool Active
        {
            get
            {
                return _active;
            }
            set
            {
                if (_active != value)
                {
                    _active = value;
                    GotoNextGeneration();
                    OnPropertyChanged(new PropertyChangedEventArgs("Active"));
                }
            }
        }

        public FilterCollection()
        {
        }

        public void AddFilter(SingleFilter filter)
        {
            if (filter != null)
            {
                this.Add(filter);
            }
        }

        public void AddFilter(string filterType)
        {
            Type t = AllFilters.FindFilter(filterType);
            if (t != null)
            {
                var filter = t.CreateFilter();
                if (filter != null)
                {
                    AddFilter(new SingleFilter(filter));
                }
            }
        }

        public static FilterCollection Load(string path)
        {
            if (File.Exists(path))
            {
                byte[] bytes = File.ReadAllBytes(path);
                List<SingleFilter> singleFilters = bytes.DeserializeFromCompressedBinary() as List<SingleFilter>;
                if (singleFilters != null)
                {
                    var collection = new FilterCollection();
                    foreach (var single in singleFilters)
                    {
                        collection.AddFilter(single);
                    }
                    return collection;
                }
            }

            return null;
        }

        public List<Type> GetFilterTypes()
        {
            List<Type> results = new List<Type>();

            for (int n = 0; n < Count; n++)
            {
                results.Add(this[n].GetType());
            }

            return results;
        }

        public void Save(string path)
        {
            var results = this.ToList();            
            byte[] bytes = results.SerializeToCompressedBinary();
            File.WriteAllBytes(path, bytes);
        }

        public Bitmap Apply(BitmapData imageData)
        {
            UnmanagedImage dstUnmanagedImage = Apply(new UnmanagedImage(imageData));
            Bitmap dstImage = dstUnmanagedImage.ToManagedImage();
            dstUnmanagedImage.Dispose();

            return dstImage;
        }

        public UnmanagedImage Apply(UnmanagedImage image)
        {
            // check for empty sequence
            if (Count == 0)
                throw new ApplicationException("No filters in the sequence.");

            UnmanagedImage dstImg = image;
            UnmanagedImage tmpImg = null;

            for (int n = 0; n < Count; n++)
            {
                var filter = this[n];

                if ((filter.Status & FilterStatus.Disabled) == FilterStatus.Disabled)
                    continue;

                int tries = 0;

            TryAgain:
                try
                {
                    tmpImg = dstImg;
                    dstImg = this[n].Filter.Apply(tmpImg);
                    tmpImg.Dispose();
                }
                catch
                {
                    if (tries > 0)
                        continue;

                    System.Drawing.Imaging.PixelFormat format = filter.Filter.GetValidPixelFormat();
                    bool tookCorrectiveAction = false;

                    switch (format)
                    {
                        case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
                            tmpImg = dstImg;
                            dstImg = (new AForge.Imaging.Filters.GrayscaleBT709()).Apply(tmpImg);
                            tmpImg.Dispose();
                            tookCorrectiveAction = true;
                            break;
                        case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
                            tmpImg = dstImg;
                            dstImg = (new AForge.Imaging.Filters.GrayscaleToRGB()).Apply(tmpImg);
                            tmpImg.Dispose();
                            tookCorrectiveAction = true;
                            break;
                    }

                    if (tookCorrectiveAction)
                    {
                        tries++;
                        goto TryAgain;
                    }
                }
            }

            return dstImg;
        }


        public Bitmap ApplyFilters(Bitmap originalBitmap)
        {
            if (originalBitmap == null)
                return null;

            if (Count == 0 || !Active)
                return originalBitmap;

            var temp = originalBitmap.Clone(new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height), System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            Bitmap dstImage = null;

            // lock source bitmap data
            BitmapData imageData = originalBitmap.LockBits(new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height), ImageLockMode.ReadOnly, originalBitmap.PixelFormat);

            try
            {
                dstImage = Apply(imageData);
            }
            finally
            {
                originalBitmap.UnlockBits(imageData);
            }

            return dstImage;
        }

        public void ClearAllFilters()
        {
            Clear();
        }

        protected void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
    }
}
